﻿
Imports System.IO
Imports System.Text
Imports std
Imports streamhandle

Public Module _streamhandle4
    Private ReadOnly STREAMHANDLE4_READ_TICKS As Int64
    Private ReadOnly STREAMHANDLE4_READ_STEP_TICKS As Int64
    Private ReadOnly STREAMHANDLE4_WRITE_TICKS As Int64
    Private ReadOnly STREAMHANDLE4_TRAVACTION_TICKS As Int64

    Sub New()
        If isdebugmode() Then
            STREAMHANDLE4_READ_TICKS =
                counter.registerAverageAndLastAverage("STREAMHANDLE4_READ_TICKS")
            STREAMHANDLE4_READ_STEP_TICKS =
                counter.registerAverageAndLastAverage("STREAMHANDLE4_READ_STEP_TICKS")
            STREAMHANDLE4_WRITE_TICKS =
                counter.registerAverageAndLastAverage("STREAMHANDLE4_WRITE_TICKS")
            STREAMHANDLE4_TRAVACTION_TICKS =
                counter.registerAverageAndLastAverage("STREAMHANDLE4_TRAVACTION_TICKS")
        End If
    End Sub

    Public Delegate Function trav_f(ByVal d() As Byte, ByVal count As Int64, ByVal holdticks As Int64) As eventComb
    Public Function traversalstream4(ByVal s As Stream, ByVal trav As trav_f,
                                     Optional ByVal offsetInStream As Int64 = npos,
                                     Optional ByVal length As Int64 = npos,
                                     Optional ByVal starttime As Int64 = npos,
                                     Optional ByVal holdtime As Int32 = maxInt32,
                                     Optional ByRef ec As eventComb = Nothing,
                                     Optional ByVal result As pointer(Of Int64) = Nothing) As Boolean
        Dim rtn As ternary = Nothing
        rtn = initial_traversal(s, offsetInStream, length, starttime, holdtime)
        If rtn.notunknown() Then
            Return rtn.true_()
        End If
        If trav Is Nothing Then
            raiseError("no valid trav_f defined.", errorHandle.errorType.warning)
            Return False
        End If

        Dim buff As bytes = Nothing
        buff = create_buff()
        Dim readcount As Int64 = 0
        Dim lasttime As Int64 = 0
        Dim readec As eventComb = Nothing
        Dim travec As eventComb = Nothing
        readec = New eventCombAsyncOperation(
                         Function(ByRef ac As AsyncCallback,
                                  ByRef obj As Object) As IAsyncResult
                             assert(travec Is Nothing OrElse travec.end())
                             Return read_begin(s, length, readcount, buff, ac, obj)
                         End Function,
                         Sub(ByRef ar As IAsyncResult)
                             read_end(ar, lasttime, s)
                             If isdebugmode() Then
                                 counter.recordTimeTicks(STREAMHANDLE4_READ_STEP_TICKS, readec.begin_ticks())
                             End If
                         End Sub)
        ec = New eventComb(Function() As Boolean
                               assert(travec Is Nothing OrElse travec.end())
                               assert(readec.not_started())
                               If isdebugmode() AndAlso Not travec Is Nothing Then
                                   counter.recordTimeTicks(STREAMHANDLE4_TRAVACTION_TICKS, travec.begin_ticks())
                               End If
                               If length = npos OrElse readcount < length Then
                                   Return [goto](1) AndAlso
                                          waitfor(readec, timeoutms(starttime, holdtime))

                               Else
                                   Return [goto](2)
                               End If
                           End Function,
                           Function() As Boolean
                               assert(readec.end())
                               If readec.end_result() AndAlso
                                  lasttime > 0 AndAlso
                                  (timeoutms(starttime, holdtime) > 0 OrElse
                                  (length > npos AndAlso readcount > 0 AndAlso
                                   timeoutms(starttime, holdtime << 1) > 0)) Then
                                   travec = trav(+buff, lasttime, millisecondsToTicks(timeoutms(starttime, holdtime)))
                                   readcount += lasttime
                                   readec.reset()
                                   Return (travec Is Nothing OrElse waitfor(travec)) AndAlso [goto](0)
                               Else
                                   Return [goto](2)
                               End If
                           End Function,
                           Function() As Boolean
                               assert(travec Is Nothing OrElse travec.end())
                               assert(readec.end() OrElse readec.not_started())
                               'end is coming from step1, when readec.end_result() = false
                               'not_started is coming from step0, when readcount = length
                               If Not result Is Nothing Then
                                   result.set(readcount)
                               End If
                               If isdebugmode() Then
                                   counter.recordTimeTicks(STREAMHANDLE4_READ_TICKS, eventComb.current().begin_ticks())
                               End If
                               buff.clear()
                               Return (length = npos OrElse length = readcount) AndAlso goto_end()
                           End Function)
        Return True
    End Function

    Public Function writestream4(ByVal o As Stream, ByVal d() As Byte,
                                 Optional ByVal length As Int64 = npos,
                                 Optional ByVal starttime As Int64 = npos,
                                 Optional ByVal holdtime As Int32 = maxInt32,
                                 Optional ByRef ec As eventComb = Nothing) As Boolean
        Dim rtn As ternary = Nothing
        rtn = initial_write(o, d, 0, length, starttime, holdtime)
        If rtn.notunknown() Then
            Return rtn.true_()
        End If

        Dim writeec As eventComb = Nothing
        writeec = New eventCombAsyncOperation(
                        Function(ByRef ac As AsyncCallback, ByRef obj As Object) As IAsyncResult
                            Return write_begin(o, d, length, ac, obj)
                        End Function,
                        Sub(ByRef ar As IAsyncResult)
                            write_end(o, ar)
                            If isdebugmode() Then
                                counter.recordTimeTicks(STREAMHANDLE4_WRITE_TICKS, eventComb.current().begin_ticks())
                            End If
                        End Sub)
        ec = New eventComb(Function() As Boolean
                               Return goto_next() AndAlso
                                      waitfor(writeec, timeoutms(starttime, holdtime))
                           End Function,
                           Function() As Boolean
                               Return writeec.end_result() AndAlso goto_end()
                           End Function)
        Return True
    End Function

    Private Function _readstream(ByVal i As Stream,
                                 ByVal offsetInStream As Int64,
                                 ByVal length As Int64,
                                 ByVal starttime As Int64,
                                 ByVal holdtime As Int32,
                                 ByRef ec As eventComb,
                                 ByVal writer As trav_f,
                                 ByVal finish As void) As Boolean
        assert(Not writer Is Nothing)
        assert(Not finish Is Nothing)
        Dim p As pointer(Of Int64) = Nothing
        p = New pointer(Of Int64)()
        Dim travec As eventComb = Nothing
        If traversalstream4(i, writer, offsetInStream, length, starttime, holdtime, travec, p) Then
            ec = New eventComb(Function() As Boolean
                                   Return waitfor(travec) AndAlso [goto](1)
                               End Function,
                               Function() As Boolean
                                   Return eventComb.success_behavior(
                                              travec,
                                              Sub()
                                                  assert(length < 0 OrElse +p = length)
                                                  finish()
                                                  goto_end()
                                              End Sub)
                               End Function)
            Return True
        Else
            Return False
        End If
    End Function

    Private Class readstreamHandler
        Private i As vector(Of pointer(Of bytes)) = Nothing

        Public Sub New()
            i = New vector(Of pointer(Of bytes))()
        End Sub

        Public Function write(ByVal d() As Byte, ByVal count As Int64, ByVal holdticks As Int64) As eventComb
            assert(i.push_back(New pointer(Of bytes)(New bytes(d, 0, count))))
            Return Nothing
        End Function

        Public Function output() As vector(Of pointer(Of bytes))
            Return i
        End Function
    End Class

    Public Function readstream4(ByVal i As Stream,
                                Optional ByVal offsetInStream As Int64 = npos,
                                Optional ByVal length As Int64 = npos,
                                Optional ByVal starttime As Int64 = npos,
                                Optional ByVal holdtime As Int32 = maxInt32,
                                Optional ByRef ec As eventComb = Nothing,
                                Optional ByVal result As pointer(Of bytes) = Nothing) As Boolean
        Dim handler As readstreamHandler = Nothing
        handler = New readstreamHandler()
        Return _readstream(i, offsetInStream, length, starttime, holdtime, ec, AddressOf handler.write,
                           Sub()
                               If Not result Is Nothing Then
                                   Dim bts As vector(Of pointer(Of bytes)) = Nothing
                                   bts = handler.output()
                                   Dim bt As bytes = Nothing
                                   Dim len As Int64 = 0
                                   For j As Int64 = 0 To bts.size() - 1
                                       len += (+(bts(j))).size()
                                   Next
                                   bt = New bytes()
                                   bt.size() = len
                                   len = 0
                                   For j As Int64 = 0 To bts.size() - 1
                                       Dim b As bytes = Nothing
                                       b = +(bts(j))
                                       memcpy(+bt, len, +b, 0, b.size())
                                       len += b.size()
                                   Next
                                   result.setinstance(bt)
                               End If
                           End Sub)
    End Function

    Public Function streammove4(ByVal i As Stream,
                                ByVal from As Int64,
                                ByVal [to] As Int64,
                                Optional ByVal length As Int64 = npos,
                                Optional ByVal starttime As Int64 = npos,
                                Optional ByVal holdtime As Int32 = maxInt32,
                                Optional ByRef ec As eventComb = Nothing,
                                Optional ByVal result As pointer(Of Int64) = Nothing) As Boolean
        If i Is Nothing OrElse Not i.CanRead() OrElse Not i.CanWrite() OrElse Not i.CanSeek() Then
            raiseError(errorHandle.errorType.warning, "input stream cannot read / write / seek.")
            Return False
        End If
        If from < [to] Then
            raiseError(errorHandle.errorType.warning, "do not support backward stream moving.")
            Return False
        End If
        If from = [to] Then
            Return True
        Else
            Dim offset As Int64 = 0
            Return traversalstream4(i,
                                    Function(d() As Byte, count As Int64, holdticks As Int64) As eventComb
                                        Dim e As eventComb = Nothing
                                        Dim curpos As Int64 = 0
                                        Return New eventComb(Function() As Boolean
                                                                 curpos = i.Position()
                                                                 i.Position() = [to] + offset
                                                                 Return writestream4(i,
                                                                                     d,
                                                                                     count,
                                                                                     starttime,
                                                                                     ticksToMilliseconds(holdticks),
                                                                                     e) AndAlso
                                                                        waitfor(e) AndAlso
                                                                        goto_next()
                                                             End Function,
                                                             Function() As Boolean
                                                                 If e.end_result() Then
                                                                     offset += count
                                                                     i.Position() = curpos
                                                                     Return goto_end()
                                                                 Else
                                                                     Return False
                                                                 End If
                                                             End Function)
                                    End Function,
                                    from,
                                    length,
                                    starttime,
                                    holdtime,
                                    ec,
                                    result)
        End If
    End Function
End Module
